#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Datetime: 2021/11/06 19:07
# @Author  : CHEN Wang
# @Site    :
# @File    : timing_signal_backtest.py
# @Software: PyCharm

"""
脚本说明: 单资产时间序列上的择时回测(输入信号)
"""

import os
import numpy as np
import pandas as pd
from quant_researcher.quant.project_tool.localize import TEST_DIR
from quant_researcher.quant.datasource_fetch.crypto_api.glassnode import get_ret, get_prices
from quant_researcher.quant.performance_attribution.core_functions.performance_analysis import performance
from quant_researcher.quant.backtest.holding_backtest import simple_backtest
from quant_researcher.quant.project_tool.time_tool import get_today
from quant_researcher.quant.factors.factor_analysis.factor_analyser.crypto_timing_signal_generator import timing_signal_generator


def timing_signal_becktest(signal, asset='BTC', start_date=None, end_date=None, commission=0.001):
    """

    :param pd.Series signal: 仓位信号，注意这里是已经滞后过一期的，也即这里的仓位信号是再上一交易日完成调仓的
        —————————————
        2010-01-03 |  1
        2010-01-04 | 0
        2010-01-05 | 0
        2010-01-06 | 1
        —————————————

    :param str asset: 默认为‘BTC’
    :param start_date:
    :param end_date:
    :param commission:
    :return:
    """

    trade_date_signal = signal[signal != signal.shift(1)]
    today = get_today(marker='with_n_dash')
    file_path = os.path.join(TEST_DIR, f'all_selected_strategy_signal-{today}')
    os.makedirs(file_path, exist_ok=True)
    file_name1 = os.path.join(file_path, f'{signal.name}-signal_trade_date')
    trade_date_signal.to_excel(f'{file_name1}.xlsx')

    asset_weights_df = pd.DataFrame(signal).reset_index()
    asset_weights_df.columns = ['tradedate', 'weights']
    asset_weights_df['code'] = asset

    # 回测开始结束时间
    if start_date is None:
        start_date = signal.index[0]
    if end_date is None:
        end_date = signal.index[-1]

    file_name2 = os.path.join(file_path, f'{signal.name}-signal_backtest')
    portfolio_equity, benchmark_equity, portfolio_ret, perf = simple_backtest(
        asset_weights_df=asset_weights_df,
        asset_ret_df=None,
        asset_type='crypto',
        start_date=start_date,
        end_date=end_date,
        benchmark=asset,
        benchmark_type='crypto',
        benchmark_ret_df=None,
        if_plot=False,
        file_name=file_name2,
        commission=commission,
        log_ret=True)

    if set(signal.unique()) == {0, 1}:  # 信号仓位只有0和1
        all_asset_ret = get_ret([asset], start_date, end_date)  # 获取该资产与USDT收益率数据
        asset_ret_series = all_asset_ret[asset]  # 提取该资产收益率数据，进行后续回测胜率检验
        # 交易胜率分析
        winning_test_data = pd.concat([signal, asset_ret_series], axis=1).dropna()
        winning_test_data.columns = ['signal', 'ret']
        win_rate_analysis = performance.winning_rate(winning_test_data)
        win_rate_analysis.name = '组合策略绩效'
        win_rate_analysis = pd.DataFrame(win_rate_analysis)
        win_rate_analysis[perf.columns[1]] = np.NAN
        perf = pd.concat([perf, win_rate_analysis], axis=0)

    perf.rename(columns={'组合策略绩效': f'{signal.name}'}, inplace=True)
    file_name = os.path.join(file_path, f'{signal.name}-策略绩效')
    perf.to_excel(f'{file_name}.xlsx')

    return portfolio_equity, benchmark_equity, portfolio_ret, perf


if __name__ == '__main__':
    asset = 'BTC'
    start_date = '2015-01-01'
    end_date = '2021-11-06'

    # 获取特定指标策略信号
    # factor_name = 'Realized Loss'
    # timing_signal_method = pd.Series(['origin', 'detrend_denoise', 'full_history', 'threshold', [-227592631, 32073836, 551406770], False],
    #                                  index=['factor_type', 'preprocess', 'signal_method', 'signal_type', 'bins', 'positive'])
    # signal = timing_signal_generator(factor_name, asset, start_date, end_date, timing_signal_method)

    # 获取均线策略信号
    prices_df = get_prices(ohlc=True, asset=asset, start_date=start_date, end_date=end_date)

    prices_df['ma140'] = prices_df['close'].rolling(window=140, min_periods=140).mean()
    signal = prices_df['ma140'].copy()
    signal[prices_df['low'] > prices_df['ma140']] = 'buy'
    signal[prices_df['high'] < prices_df['ma140']] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    # 前面的NaN直接踢掉，后面的NaN用前值填充
    signal.fillna(method='ffill', inplace=True)
    signal.dropna(inplace=True)
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'high_ma140_low'

    # prices_df['ma120'] = prices_df['close'].rolling(window=120, min_periods=120).mean()
    # signal = prices_df['ma120'].copy()
    # signal[prices_df['low'] > prices_df['ma120']] = 'buy'
    # signal[prices_df['high'] < prices_df['ma120']] = 'sell'
    # signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    # # 前面的NaN直接踢掉，后面的NaN用前值填充
    # signal.fillna(method='ffill', inplace=True)
    # signal.dropna(inplace=True)
    # signal[signal == 'buy'] = 1
    # signal[signal == 'sell'] = 0
    # signal.name = 'high_ma120_low'

    # signal 需要滞后一期使用
    signal = signal.shift(1)
    signal.dropna(inplace=True)

    # 测试timing_signal_becktest
    file_path = os.path.join(TEST_DIR, '单指标信号择时回测')
    os.makedirs(file_path, exist_ok=True)
    file_name = os.path.join(file_path, f'{signal.name}')
    signal.to_excel(f'{file_name}.xlsx')
    timing_signal_becktest(signal)